package org.example.domain.plugin.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.example.domain.plugin.service.BaseAsyncPlugin;
import org.example.types.model.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * PanSearch插件实现
 */
@Slf4j
@Component
public class PanSearchPlugin extends BaseAsyncPlugin {

    private static final String BASE_URL = "https://www.pansearch.me/search";
    private static final String API_URL = "https://www.pansearch.me/_next/data/%s/search.json";

    private static final Pattern BUILD_ID_PATTERN = Pattern.compile("\"buildId\":\"([^\"]+)\"");
    private static final Pattern LINK_PATTERN = Pattern.compile("href=\"([^\"]+)\"");
    private static final Pattern PASSWORD_PATTERN = Pattern.compile("\\?pwd=([^\"&#]+)");

    private volatile String buildId;
    private volatile long buildIdTimestamp;
    private static final long BUILD_ID_CACHE_DURATION = 30 * 60 * 1000; // 30分钟

    @Override
    public String getName() {
        return "pansearch";
    }

    @Override
    public int getPriority() {
        return 2; // 中高优先级
    }

    @Override
    protected Mono<PluginSearchResult> doSearch(String keyword, SearchContext context, Map<String, Object> ext) {
        return getBuildId()
                .flatMap(id -> searchWithBuildId(keyword, id))
                .map(this::createSuccessResult)
                .onErrorResume(ex -> {
                    log.warn("PanSearch plugin search failed for keyword: {}", keyword, ex);
                    return Mono.just(createErrorResult("搜索失败: " + ex.getMessage()));
                });
    }

    /**
     * 获取BuildId
     */
    private Mono<String> getBuildId() {
        // 检查缓存
        if (buildId != null && (System.currentTimeMillis() - buildIdTimestamp) < BUILD_ID_CACHE_DURATION) {
            return Mono.just(buildId);
        }

        return fastWebClient.get()
                .uri(BASE_URL)
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
                .retrieve()
                .bodyToMono(String.class)
                .map(this::extractBuildId)
                .doOnNext(id -> {
                    this.buildId = id;
                    this.buildIdTimestamp = System.currentTimeMillis();
                    log.debug("Updated buildId: {}", id);
                })
                .onErrorResume(ex -> {
                    log.warn("Failed to get buildId, using cached value", ex);
                    return buildId != null ? Mono.just(buildId) : Mono.error(ex);
                });
    }

    /**
     * 从HTML中提取BuildId
     */
    private String extractBuildId(String html) {
        Matcher matcher = BUILD_ID_PATTERN.matcher(html);
        if (matcher.find()) {
            return matcher.group(1);
        }

        // 尝试从__NEXT_DATA__中提取
        try {
            Document doc = Jsoup.parse(html);
            Elements scripts = doc.select("script#__NEXT_DATA__");
            if (!scripts.isEmpty()) {
                String jsonData = scripts.first().data();
                matcher = Pattern.compile("\"buildId\":\"([^\"]+)\"").matcher(jsonData);
                if (matcher.find()) {
                    return matcher.group(1);
                }
            }
        } catch (Exception e) {
            log.debug("Failed to parse __NEXT_DATA__", e);
        }

        throw new RuntimeException("BuildId not found in response");
    }

    /**
     * 使用BuildId进行搜索
     */
    private Mono<List<SearchResult>> searchWithBuildId(String keyword, String buildId) {
        String apiUrl = String.format(API_URL, buildId);

        return webClient.get()
                .uri(uriBuilder -> uriBuilder
                    .path(apiUrl)
                    .queryParam("keyword", keyword)
                    .queryParam("offset", 0)
                    .build())
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
                .header("Referer", "https://www.pansearch.me/")
                .retrieve()
                .bodyToMono(PanSearchResponse.class)
                .map(response -> convertToSearchResults(response, keyword))
                .doOnError(WebClientResponseException.class, ex -> {
                    if (ex.getStatusCode().value() == 404) {
                        log.info("BuildId expired, will refresh on next request");
                        this.buildId = null; // 清除缓存的buildId
                    }
                });
    }

    /**
     * 转换API响应为SearchResult
     */
    private List<SearchResult> convertToSearchResults(PanSearchResponse response, String keyword) {
        List<SearchResult> results = new ArrayList<>();

        if (response.getPageProps() == null ||
            response.getPageProps().getData() == null ||
            response.getPageProps().getData().getData() == null) {
            return results;
        }

        for (PanSearchItem item : response.getPageProps().getData().getData()) {
            try {
                SearchResult result = convertItem(item, keyword);
                if (result != null) {
                    results.add(result);
                }
            } catch (Exception e) {
                log.debug("Failed to convert item: {}", item.getId(), e);
            }
        }

        return filterResults(results, keyword);
    }

    /**
     * 转换单个项目
     */
    private SearchResult convertItem(PanSearchItem item, String keyword) {
        if (item.getContent() == null || item.getContent().trim().isEmpty()) {
            return null;
        }

        // 解析链接和密码
        LinkInfo linkInfo = extractLinkAndPassword(item.getContent());
        if (linkInfo.getUrl() == null || linkInfo.getUrl().isEmpty()) {
            return null;
        }

        // 创建链接
        Link link = Link.builder()
                .url(linkInfo.getUrl())
                .password(linkInfo.getPassword())
                .type(mapPanType(item.getPan()))
                .build();

        // 解析时间
        LocalDateTime datetime = parseDateTime(item.getTime());

        // 提取标题
        String title = extractTitle(item.getContent(), keyword);

        return SearchResult.builder()
                .uniqueId("pansearch-" + item.getId())
                .title(title)
                .content(cleanContent(item.getContent()))
                .datetime(datetime)
                .links(List.of(link))
                .build();
    }

    /**
     * 提取链接和密码
     */
    private LinkInfo extractLinkAndPassword(String content) {
        LinkInfo info = new LinkInfo();

        // 提取链接
        Matcher linkMatcher = LINK_PATTERN.matcher(content);
        if (linkMatcher.find()) {
            info.setUrl(linkMatcher.group(1));
        }

        // 提取密码
        Matcher pwdMatcher = PASSWORD_PATTERN.matcher(content);
        if (pwdMatcher.find()) {
            info.setPassword(pwdMatcher.group(1));
        }

        return info;
    }

    /**
     * 提取标题
     */
    private String extractTitle(String content, String keyword) {
        try {
            Document doc = Jsoup.parse(content);

            // 查找名称字段
            String title = doc.text();
            if (title.contains("名称：")) {
                int start = title.indexOf("名称：") + 3;
                int end = title.indexOf("\n", start);
                if (end == -1) end = title.length();
                title = title.substring(start, end).trim();
            }

            // 清理HTML标签
            title = Jsoup.parse(title).text();

            return StringUtils.isNotBlank(title) ? title : keyword;
        } catch (Exception e) {
            return keyword;
        }
    }

    /**
     * 清理内容
     */
    private String cleanContent(String content) {
        try {
            return Jsoup.parse(content).text();
        } catch (Exception e) {
            return content;
        }
    }

    /**
     * 映射网盘类型
     */
    private String mapPanType(String panType) {
        if (panType == null) return "others";

        return switch (panType.toLowerCase()) {
            case "aliyundrive" -> "aliyun";
            case "baidu" -> "baidu";
            case "quark" -> "quark";
            case "tianyi" -> "tianyi";
            case "uc" -> "uc";
            case "115" -> "115";
            case "pikpak" -> "pikpak";
            case "xunlei" -> "xunlei";
            case "123" -> "123";
            default -> "others";
        };
    }

    /**
     * 解析时间
     */
    private LocalDateTime parseDateTime(String timeStr) {
        if (StringUtils.isBlank(timeStr)) {
            return null;
        }

        try {
            return LocalDateTime.parse(timeStr, DateTimeFormatter.ISO_DATE_TIME);
        } catch (Exception e) {
            log.debug("Failed to parse datetime: {}", timeStr);
            return null;
        }
    }

    // 内部类定义
    private static class LinkInfo {
        private String url;
        private String password;

        public String getUrl() { return url; }
        public void setUrl(String url) { this.url = url; }
        public String getPassword() { return password; }
        public void setPassword(String password) { this.password = password; }
    }

    // API响应模型类
    private static class PanSearchResponse {
        private PageProps pageProps;

        public PageProps getPageProps() { return pageProps; }
        public void setPageProps(PageProps pageProps) { this.pageProps = pageProps; }
    }

    private static class PageProps {
        private DataWrapper data;

        public DataWrapper getData() { return data; }
        public void setData(DataWrapper data) { this.data = data; }
    }

    private static class DataWrapper {
        private int total;
        private List<PanSearchItem> data;

        public int getTotal() { return total; }
        public void setTotal(int total) { this.total = total; }
        public List<PanSearchItem> getData() { return data; }
        public void setData(List<PanSearchItem> data) { this.data = data; }
    }

    private static class PanSearchItem {
        private int id;
        private String content;
        private String pan;
        private String time;

        public int getId() { return id; }
        public void setId(int id) { this.id = id; }
        public String getContent() { return content; }
        public void setContent(String content) { this.content = content; }
        public String getPan() { return pan; }
        public void setPan(String pan) { this.pan = pan; }
        public String getTime() { return time; }
        public void setTime(String time) { this.time = time; }
    }
}